<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>WebSocket</title>
	</head>
	<body>
		<script>
			{
				// WebSocket构造函数，创建WebSocket对象
				// let ws = new WebSocket('ws://localhost:8848')
				let ws = new WebSocket('ws://example.org')

				// 连接成功后的回调函数
				ws.onopen = function(e) {
					// ws.addEventListener('open', function(e) {
					console.log('客户端连接成功')
					// 向服务器发送消息
					ws.send('hello')
				};

				// 从服务器接受到信息时的回调函数
				ws.onmessage = function(e) {
					// ws.addEventListener("message", function(e) {
					console.log('收到服务器响应', e.data)
					if (typeof e.data === String) {
						console.log("返回数据是字符串");
					}
				}
				// ws.binaryType = "arraybuffer";
				// ws.onmessage = function(e) {
				// 	// 收到的是 ArrayBuffer 数据
				// 	console.log(e.data.byteLength);
				// };

				// 连接失败后的回调函数
				ws.onerror = function(e) {
					// ws.addEventListener('error', function(e) {
					console.log("连接失败了");
				};

				// 连接关闭后的回调函数
				ws.onclose = function(e) {
					// ws.addEventListener("close", function(e) {
					var code = e.code; // 关闭码
					var reason = e.reason; // 关闭的原因
					var wasClean = e.wasClean; // 否完全关闭
					console.log("关闭客户端连接", reason);
				};


				// 监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，这样服务端会抛异常。
				window.onbeforeunload = function() {
					ws.close();
				}

				switch (ws.readyState) {
					case WebSocket.CONNECTING: // 0 正在连接
						break;
					case WebSocket.OPEN: // 1 连接成功
						break;
					case WebSocket.CLOSING: // 2 连接正在关闭
						break;
					case WebSocket.CLOSED: // 3 连接已经关闭，或打开连接失败
						break;
					default:
						break;
				}
			}

			{
				// 情景： 前端使用 WebSocket 的时候，后端长时间没有推送数据，导致 WebSocket 连接经常断开，后端也会报错。
				// 解决方法： 通过 心跳机制 让前端和后端始终保持连接。
				// 注意： 后端收到以后需要给前端返回数据，否则还是无法保持连接

				// WebSocket
				var ws = null;
				// WebSocket连接地址
				var ws_url = 'ws://localhost:8848'

				// 创建WebSocket
				ws_create(ws_url);

				// 创建WebSocket
				function ws_create(url) {
					try {
						// 判断是否支持 WebSocket
						if ('WebSocket' in window) {
							// 连接WebSocket
							ws = new WebSocket(url);
							// 初始化WebSocket事件(WebSocket对象, WebSocket连接地址)
							ws_event(ws, url);
						}
					} catch (e) {
						// 重新连接WebSocket
						ws_recontent(url);
						console.log(e);
					}
				}

				// WebSocket 事件创建
				function ws_event(ws, url) {
					ws.onopen = function(event) {
						// 心跳检测重置
						ws_heartCheck.reset().start();
						console.log("WebSocket已连接");
					};

					ws.onclose = function(event) {
						// 重新连接WebSocket
						ws_recontent(url);
						console.log("WebSocket连接已关闭");
					};

					ws.onerror = function(event) {
						// 重新连接WebSocket
						ws_recontent(url);
						console.log("WebSocket错误：", event);
					};

					ws.onmessage = function(event) {
						// 只要有数据，那就说明连接正常
						ws_heartCheck.reset().start();

						// 处理数据，只处理非心跳检测的数据
						if (event.data != 'check') {
							// 处理数据
						}
					};
				}

				// 重新连接websocker(WebSocket连接地址)
				function ws_recontent(url) {
					// 延迟避免请求过多
					setTimeout(function() {
						ws_create(url);
					}, 2000);
				}

				// 监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，这样服务端会抛异常。
				window.onbeforeunload = function() {
					ws.close();
				}

				// WebSocket心跳检测
				var ws_heartCheck = {
					timeout: 5000, // 5秒一次心跳
					timeoutObj: null, // 执行心跳的定时器
					serverTimeoutObj: null, // 服务器超时定时器
					reset: function() { // 重置方法
						clearTimeout(this.timeoutObj);
						clearTimeout(this.serverTimeoutObj);
						return this;
					},
					start: function() { // 启动方法
						var self = this;
						this.timeoutObj = setTimeout(function() {
							// 这里发送一个心跳信息，后端收到后，返回一个消息，在onmessage拿到返回的心跳（信息）就说明连接正常
							// 心跳包内容
							var heartBeat = {
								type: "ping",
								timestamp: new Date().getTime()
							}
							// ws.send(JSON.stringify(heartBeat));
							ws.send("check");
							
							// 如果超过一定时间还没重置，说明后端主动断开了
							self.serverTimeoutObj = setTimeout(function() {
								// 如果onclose会执行reconnect，我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
								ws.close();
							}, self.timeout);
						}, this.timeout);
					}
				}
			}
			
			{
				// 创建 WebSocket 连接
				const socket = new WebSocket('wss://example.com');
			
				// 定义心跳包数据
				const heartbeatData = 'heartbeat';
			
				// 定时发送心跳包
				const heartbeatInterval = setInterval(() => {
					if (socket.readyState === WebSocket.OPEN) {
						socket.send(heartbeatData);
					}
				}, 5000); // 每隔 5 秒发送一次心跳包
			
				// 监听消息
				socket.addEventListener('message', (event) => {
					const message = event.data;
			
					// 判断收到的消息是否是心跳包
					if (message === heartbeatData) {
						// 收到心跳包，更新连接状态
						console.log('Received heartbeat from server.');
					} else {
						// 处理其他消息
						console.log('Received message from server:', message);
					}
				});
			
				// 监听连接关闭事件
				socket.addEventListener('close', (event) => {
					clearInterval(heartbeatInterval);
					console.log('WebSocket connection closed with code:', event.code);
				});
			}
		</script>
	</body>
</html>